home *** CD-ROM | disk | FTP | other *** search
/ Space & Astronomy / Space and Astronomy (October 1993).iso / mac / VIEWERS / MSDOS / GIFLIB12.ZIP / UTIL / GIF2HERC.C < prev    next >
C/C++ Source or Header  |  1991-05-12  |  36KB  |  1,109 lines

  1. /*****************************************************************************
  2. *   "Gif-Lib" - Yet another gif library.                     *
  3. *                                         *
  4. * Written by:  Gershon Elber                Ver 0.1, Jul. 1989   *
  5. ******************************************************************************
  6. * Program to display GIF file on hercules device                 *
  7. * Options:                                     *
  8. * -q : quite printing mode.                             *
  9. * -z factor : zoom the pixels by the given factor.                 *
  10. * -t level : set the threshold level of white in the result (0..100).         *
  11. * -m mapping : methods for mapping the 24bits colors into 1 BW bit.         *
  12. * -i : invert the image.                             *
  13. * -b : beeps disabled.                                 *
  14. * -h : on line help.                                 *
  15. *                                         *
  16. *   This program uses TC2.0 hercules graphic driver.                 *
  17. *   In this file Screen refers to GIF file screen, while Device to Hercules. *
  18. ******************************************************************************
  19. * History:                                     *
  20. * 1 Jul 89 - Version 1.0 by Gershon Elber.                     *
  21. *****************************************************************************/
  22.  
  23. #include <graphics.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <conio.h>
  27. #include <ctype.h>
  28. #include <alloc.h>
  29. #include <string.h>
  30. #include <io.h>
  31. #include <dos.h>
  32. #include <bios.h>
  33. #include <fcntl.h>
  34. #include "gif_lib.h"
  35. #include "getarg.h"
  36.  
  37. #define PROGRAM_NAME    "Gif2Herc"
  38.  
  39. #define KEY_LEFT    256      /* Key Codes returned for operational keys */
  40. #define KEY_RIGHT    257      /* as return by the GetKey routine.         */
  41. #define KEY_UP        258
  42. #define KEY_DOWN    259
  43. #define KEY_RETURN    260
  44. #define KEY_DELETE    261
  45. #define KEY_INSERT    262
  46. #define KEY_BSPACE    263
  47. #define KEY_ESC        264
  48. #define KEY_HOME    265
  49. #define KEY_END        266
  50. #define KEY_PGUP    267
  51. #define KEY_PGDN    268
  52.  
  53. #define    C2BW_BACK_GROUND    0 /*Methods to map 24bits Colors to 1 BW bit.*/
  54. #define    C2BW_GREY_LEVELS    1
  55. #define C2BW_DITHER        2
  56. #define C2BW_NUM_METHODS    3            /* Always hold # of methods. */
  57.  
  58. #define DEFAULT_THRESHOLD    5000         /* Color -> BW threshold level. */
  59. #define INCREMENT_THRESHOLD    1000
  60.  
  61. #define DITHER_MIN_MATRIX    2
  62. #define DITHER_MAX_MATRIX    4
  63.  
  64. #define NORMAL_ATTR    0x07                 /* Text attributes. */
  65. #define INVERSE_ATTR    0x70
  66. #define BLINK_ATTR    0x90
  67.  
  68. #define SET_POSITION_RESET    0        /* Situations need positionings: */
  69. #define SET_POSITION_ZOOM_U    1
  70. #define SET_POSITION_ZOOM_D    2
  71. #define SET_POSITION_PAN    3
  72.  
  73. #define DEVICE_BASE    0xb000              /* Hercules frame buffer base. */
  74. #define DEVICE_PAGE0    0xb000
  75. #define DEVICE_PAGE1    0xb800
  76. #define HERC_MAX_X    719
  77. #define HERC_MAX_Y    347
  78.  
  79. #define CURSOR_TEXT_X    120
  80.  
  81. extern unsigned int
  82.     _stklen = 16384;                 /* Increase default stack size. */
  83.  
  84. static char
  85.     *VersionStr =
  86.     PROGRAM_NAME
  87.     GIF_LIB_VERSION
  88.     "    Gershon Elber,    "
  89.     __DATE__ ",   " __TIME__ "\n"
  90.     "(C) Copyright 1989 Gershon Elber, Non commercial use only.\n";
  91. static char
  92.     *CtrlStr =
  93.     PROGRAM_NAME
  94.     " q%- d%-DitherSize!d z%-ZoomFactor!d t%-BWThreshold!d m%-Mapping!d i%- b%- h%- GifFile!*s";
  95. static char
  96.     *GifFileName;
  97. /* Make some variables global, so we could access them faster: */
  98. static int
  99.     ImageNum = 0,
  100.     BackGround = 0,
  101.     BeepsDisabled = FALSE,
  102.     DitherSize = 2, DitherFlag = FALSE,
  103.     ZoomFactor = 1, ZoomFlag = FALSE,
  104.     BWThresholdFlag = FALSE, Threshold,
  105.     BWThreshold = DEFAULT_THRESHOLD,       /* Color -> BW mapping threshold. */
  106.     Mapping, MappingFlag = FALSE,
  107.     InvertFlag = FALSE,
  108.     HelpFlag = FALSE,
  109.     ColorToBWMapping = C2BW_BACK_GROUND,
  110.     InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should  */
  111.     InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
  112. static GifColorType
  113.     *ColorMap;
  114.  
  115.  
  116. static void DisplayScreen(GifRowType *ScreenBuffer, GifFileType *GifFile);
  117. static void PrintSettingStatus(GifFileType *GifFile, GifRowType *DitherBuffer);
  118. static void CPrintStr(char *Str, int y, int attr);
  119. static void SetPositon(int Why,
  120.                int ScreenWidth, int ScreenHeight,
  121.                int DeviceMaxX,  int DeviceMaxY,
  122.                int *ScreenLeft, int *ScreenTop,
  123.                int *DeviceLeft, int *DeviceTop,
  124.                int MoveX,       int MoveY);
  125. static void ClearGraphDevice(void);
  126. static void OpenGraphDevice(void);
  127. static void CloseGraphDevice(void);
  128. static void EvalDitheredScanline(GifRowType *ScreenBuffer, int Row,
  129.                     int RowSize, GifRowType *DitherBuffer);
  130. static void DrawScreen(GifRowType *ScreenBuffer, GifRowType *DitherBuffer,
  131.     int DeviceTop, int DeviceLeft, int DeviceMaxX, int DeviceMaxY,
  132.     int ScreenTop, int ScreenLeft, int ScreenWidth, int ScreenHeight);
  133. static void DoCursorMode(GifRowType *ScreenBuffer,
  134.     int ScreenLeft, int ScreenTop, int ScreenWidth, int ScreenHeight,
  135.     int DeviceLeft, int DeviceTop);
  136. static int MyKbHit(void);
  137. static int MyGetCh(void);
  138. static int GetKey(void);
  139. static void Tone(int Frequency, int Time);
  140.  
  141. /******************************************************************************
  142. * Interpret the command line and scan the given GIF file.              *
  143. ******************************************************************************/
  144. void main(int argc, char **argv)
  145. {
  146.     int    i, j, Error, NumFiles, Size, Row, Col, Width, Height, ExtCode, Count;
  147.     GifRecordType RecordType;
  148.     GifByteType *Extension;
  149.     char **FileName = NULL;
  150.     GifRowType *ScreenBuffer;
  151.     GifFileType *GifFile;
  152.  
  153.     if ((Error = GAGetArgs(argc, argv, CtrlStr,
  154.         &GifQuitePrint, &DitherFlag, &DitherSize,
  155.         &ZoomFlag, &ZoomFactor, &BWThresholdFlag,
  156.         &Threshold, &MappingFlag, &Mapping, &InvertFlag,
  157.         &BeepsDisabled, &HelpFlag, &NumFiles, &FileName)) != FALSE ||
  158.         (NumFiles > 1 && !HelpFlag)) {
  159.     if (Error)
  160.         GAPrintErrMsg(Error);
  161.     else if (NumFiles > 1)
  162.         GIF_MESSAGE("Error in command line parsing - one GIF file please.");
  163.     GAPrintHowTo(CtrlStr);
  164.     exit(1);
  165.     }
  166.  
  167.     if (HelpFlag) {
  168.     fprintf(stderr, VersionStr);
  169.     GAPrintHowTo(CtrlStr);
  170.     exit(0);
  171.     }
  172.  
  173.     if (DitherFlag) {
  174.     /* Make sure we are o.k.: */
  175.     if (DitherSize > DITHER_MAX_MATRIX) DitherSize = DITHER_MAX_MATRIX;
  176.     if (DitherSize < DITHER_MIN_MATRIX) DitherSize = DITHER_MAX_MATRIX;
  177.     }
  178.  
  179.     /* As Threshold is in [0..100] range and BWThreshold is [0..25500]: */
  180.     if (BWThresholdFlag) {
  181.     if (Threshold > 100 || Threshold < 0)
  182.         GIF_EXIT("Threshold not in 0..100 percent.");
  183.     BWThreshold = Threshold * 255;
  184.     if (BWThreshold == 0) BWThreshold = 1;   /* Overcome divide by zero! */
  185.     }
  186.  
  187.     /* No message is emitted, but mapping method is clipped to exists method.*/
  188.     if (MappingFlag) ColorToBWMapping = Mapping % C2BW_NUM_METHODS;
  189.  
  190.     if (NumFiles == 1) {
  191.     GifFileName = *FileName;
  192.     if ((GifFile = DGifOpenFileName(*FileName)) == NULL) {
  193.         PrintGifError();
  194.         exit(-1);
  195.     }
  196.     }
  197.     else {
  198.     /* Use the stdin instead: */
  199.     GifFileName = "Stdin";
  200.     setmode(0, O_BINARY);
  201.     if ((GifFile = DGifOpenFileHandle(0)) == NULL) {
  202.         PrintGifError();
  203.         exit(-1);
  204.     }
  205.     }
  206.  
  207.     /* Allocate the screen as vector of column of rows. We cannt allocate    */
  208.     /* the all screen at once, as this broken minded CPU can allocate up to  */
  209.     /* 64k at a time and our image can be bigger than that:             */
  210.     /* Note this screen is device independent - its the screen as defined by */
  211.     /* the GIF file parameters itself.                         */
  212.     if ((ScreenBuffer = (GifRowType *)
  213.     malloc(GifFile -> SHeight * sizeof(GifRowType *))) == NULL)
  214.         GIF_EXIT("Failed to allocate memory required, aborted.");
  215.  
  216.     Size = GifFile -> SWidth * sizeof(GifPixelType);/* Size in bytes one row.*/
  217.     if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) /* First row. */
  218.     GIF_EXIT("Failed to allocate memory required, aborted.");
  219.  
  220.     for (i = 0; i < GifFile -> SWidth; i++)  /* Set its color to BackGround. */
  221.     ScreenBuffer[0][i] = GifFile -> SBackGroundColor;
  222.     for (i = 1; i < GifFile -> SHeight; i++) {
  223.     /* Allocate the other rows, andset their color to background too: */
  224.     if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL)
  225.         GIF_EXIT("Failed to allocate memory required, aborted.");
  226.  
  227.     memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
  228.     }
  229.  
  230.     /* Scan the content of the GIF file and load the image(s) in: */
  231.     do {
  232.     if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
  233.         PrintGifError();
  234.         exit(-1);
  235.     }
  236.     switch (RecordType) {
  237.         case IMAGE_DESC_RECORD_TYPE:
  238.         if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
  239.             PrintGifError();
  240.             exit(-1);
  241.         }
  242.         Row = GifFile -> ITop; /* Image Position relative to Screen. */
  243.         Col = GifFile -> ILeft;
  244.         Width = GifFile -> IWidth;
  245.         Height = GifFile -> IHeight;
  246.         GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]:     ",
  247.             PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height);
  248.         if (GifFile -> ILeft + GifFile -> IWidth > GifFile -> SWidth ||
  249.            GifFile -> ITop + GifFile -> IHeight > GifFile -> SHeight) {
  250.             fprintf(stderr, "Image %d is not confined to screen dimension, aborted\n");
  251.             exit(-2);
  252.         }
  253.         if (GifFile -> IInterlace) {
  254.             /* Need to perform 4 passes on the images: */
  255.             for (Count = i = 0; i < 4; i++)
  256.             for (j = Row + InterlacedOffset[i]; j < Row + Height;
  257.                          j += InterlacedJumps[i]) {
  258.                 GifQprintf("\b\b\b\b%-4d", Count++);
  259.                 if (DGifGetLine(GifFile, &ScreenBuffer[j][Col],
  260.                 Width) == GIF_ERROR) {
  261.                 PrintGifError();
  262.                 exit(-1);
  263.                 }
  264.             }
  265.         }
  266.         else {
  267.             for (i = 0; i < Height; i++) {
  268.             GifQprintf("\b\b\b\b%-4d", i);
  269.             if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col],
  270.                 Width) == GIF_ERROR) {
  271.                 PrintGifError();
  272.                 exit(-1);
  273.             }
  274.             }
  275.         }
  276.         break;
  277.         case EXTENSION_RECORD_TYPE:
  278.         /* Skip any extension blocks in file: */
  279.         if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
  280.             PrintGifError();
  281.             exit(-1);
  282.         }
  283.         while (Extension != NULL) {
  284.             if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
  285.             PrintGifError();
  286.             exit(-1);
  287.             }
  288.         }
  289.         break;
  290.         case TERMINATE_RECORD_TYPE:
  291.         break;
  292.         default:            /* Should be traps by DGifGetRecordType. */
  293.         break;
  294.     }
  295.     }
  296.     while (RecordType != TERMINATE_RECORD_TYPE);
  297.  
  298.     /* Lets display it - set the global variables required and do it: */
  299.     BackGround = GifFile -> SBackGroundColor;
  300.     ColorMap = (GifFile -> IColorMap ? GifFile -> IColorMap :
  301.                        GifFile -> SColorMap);
  302.     Tone(500, 10);
  303.     DisplayScreen(ScreenBuffer, GifFile);
  304.  
  305.     if (DGifCloseFile(GifFile) == GIF_ERROR) {
  306.     PrintGifError();
  307.     exit(-1);
  308.     }
  309. }
  310.  
  311. /******************************************************************************
  312. * Given the screen buffer, display it:                          *
  313. * The following commands are available (case insensitive).              *
  314. * 1. Four arrow to move along the screen (only if ScreenBuffer > physical     *
  315. *    screen in that direction.                              *
  316. * 2. C - goto cursor mode - print current color & position in GIF screen      *
  317. *        of the current pixel cursor is on.                      *
  318. * 3. D - zoom out by factor of 2.                          *
  319. * 4. H - halftoning dithering matrix resize.                      *
  320. * 5. I - invert the image.                              *
  321. * 6. M - toggles method of Color -> BW mapping.                      *
  322. * 7. R - redraw current image.                              *
  323. * 8. S - Print Current status/options.                          *
  324. * 9. U - zoom in by factor of 2.                          *
  325. * 10. ' ' - stop drawing current image.                          *
  326. * 11. ESC - to quit.                                  *
  327. ******************************************************************************/
  328. static void DisplayScreen(GifRowType *ScreenBuffer, GifFileType *GifFile)
  329. {
  330.     int i, j, Size,
  331.     DeviceTop, DeviceLeft,   /* Where ScreenBuffer is to mapped to ours. */
  332.     ScreenTop, ScreenLeft,  /* Porsion of ScreenBuffer to start display. */
  333.     DeviceMaxX, DeviceMaxY,              /* Physical device dimensions. */
  334.     XPanning, YPanning,         /* Amount to move using the arrows. */
  335.     GetK, DrawIt = TRUE;
  336.     GifRowType *DitherBuffer;         /* Used to save dithered pixel scanned. */
  337.  
  338.     OpenGraphDevice();
  339.     DeviceMaxX = HERC_MAX_X;            /* Read size of physical screen. */
  340.     DeviceMaxY = HERC_MAX_Y;
  341.  
  342.     XPanning = DeviceMaxX / 2;
  343.     YPanning = DeviceMaxY / 2;
  344.  
  345.     SetPositon(SET_POSITION_RESET, GifFile -> SWidth, GifFile -> SHeight,
  346.         DeviceMaxX, DeviceMaxY,
  347.         &ScreenLeft, &ScreenTop,
  348.         &DeviceLeft, &DeviceTop,
  349.         0, 0);
  350.  
  351.     /*   Allocate the buffer to save the dithered information. If fails to   */
  352.     /* allocate, set it to NULL, and no dithering will take place.         */
  353.     if ((DitherBuffer = (GifRowType *)
  354.     malloc(DITHER_MAX_MATRIX * sizeof(GifRowType *))) != NULL) {
  355.     Size = GifFile -> SWidth * sizeof(GifPixelType); /* Size of one row. */
  356.     for (i = 0; i < DITHER_MAX_MATRIX; i++) {
  357.         if ((DitherBuffer[i] = (GifRowType) malloc(Size)) == NULL) {
  358.         for (j = 0; j < i; j++) free((char *) DitherBuffer[i]);
  359.         free((char *) DitherBuffer);
  360.         DitherBuffer = NULL;
  361.         break;
  362.         }
  363.     }
  364.     }
  365.     if (DitherBuffer == NULL) {
  366.     Tone(300, 100);
  367.     Tone(100, 300);
  368.     }
  369.  
  370.     do {
  371.     if (DrawIt && !MyKbHit()) {
  372.         DrawScreen(ScreenBuffer, DitherBuffer,
  373.         DeviceTop, DeviceLeft, DeviceMaxX, DeviceMaxY,
  374.         ScreenTop, ScreenLeft, GifFile -> SWidth, GifFile -> SHeight);
  375.         Tone(2000, 200);
  376.     }
  377.     DrawIt = TRUE;
  378.     switch (GetK = GetKey()) {
  379.         case 'C':
  380.         DoCursorMode(ScreenBuffer, ScreenLeft, ScreenTop,
  381.                  GifFile -> SWidth, GifFile -> SHeight,
  382.                  DeviceLeft, DeviceTop);
  383.         DrawIt = FALSE;
  384.         break;
  385.         case 'D':
  386.         if (ZoomFactor > 1) {
  387.             ZoomFactor >>= 1;
  388.             SetPositon(SET_POSITION_ZOOM_D,
  389.             GifFile -> SWidth, GifFile -> SHeight,
  390.             DeviceMaxX, DeviceMaxY,
  391.             &ScreenLeft, &ScreenTop,
  392.             &DeviceLeft, &DeviceTop,
  393.             0, 0);
  394.         }
  395.         else {
  396.             Tone(1000, 100);
  397.             DrawIt = FALSE;
  398.         }
  399.         break;
  400.         case 'H':
  401.         if (++DitherSize > DITHER_MAX_MATRIX)
  402.             DitherSize = DITHER_MIN_MATRIX;
  403.         break;
  404.         case 'I':
  405.         InvertFlag = !InvertFlag;
  406.         break;
  407.         case 'M':
  408.         ColorToBWMapping = (ColorToBWMapping + 1) % C2BW_NUM_METHODS;
  409.  
  410.         break;
  411.         case 'R':
  412.         break;
  413.         case 'S':
  414.         PrintSettingStatus(GifFile, DitherBuffer);
  415.         break;
  416.         case 'U':
  417.         if (ZoomFactor < 256) {
  418.             ZoomFactor <<= 1;
  419.             SetPositon(SET_POSITION_ZOOM_U,
  420.             GifFile -> SWidth, GifFile -> SHeight,
  421.             DeviceMaxX, DeviceMaxY,
  422.             &ScreenLeft, &ScreenTop,
  423.             &DeviceLeft, &DeviceTop,
  424.             0, 0);
  425.         }
  426.         else {
  427.             Tone(1000, 100);
  428.             DrawIt = FALSE;
  429.         }
  430.         break;
  431.         case KEY_ESC:
  432.         break;
  433.         case KEY_LEFT:
  434.         SetPositon(SET_POSITION_PAN,
  435.             GifFile -> SWidth, GifFile -> SHeight,
  436.             DeviceMaxX, DeviceMaxY,
  437.             &ScreenLeft, &ScreenTop,
  438.             &DeviceLeft, &DeviceTop,
  439.             -XPanning, 0);
  440.         break;
  441.         case KEY_RIGHT:
  442.         SetPositon(SET_POSITION_PAN,
  443.             GifFile -> SWidth, GifFile -> SHeight,
  444.             DeviceMaxX, DeviceMaxY,
  445.             &ScreenLeft, &ScreenTop,
  446.             &DeviceLeft, &DeviceTop,
  447.             XPanning, 0);
  448.         break;
  449.         case KEY_UP:
  450.         SetPositon(SET_POSITION_PAN,
  451.             GifFile -> SWidth, GifFile -> SHeight,
  452.             DeviceMaxX, DeviceMaxY,
  453.             &ScreenLeft, &ScreenTop,
  454.             &DeviceLeft, &DeviceTop,
  455.             0, -YPanning);
  456.         break;
  457.         case KEY_DOWN:
  458.         SetPositon(SET_POSITION_PAN,
  459.             GifFile -> SWidth, GifFile -> SHeight,
  460.             DeviceMaxX, DeviceMaxY,
  461.             &ScreenLeft, &ScreenTop,
  462.             &DeviceLeft, &DeviceTop,
  463.             0, YPanning);
  464.         break;
  465.         case KEY_DELETE:
  466.         BWThreshold += INCREMENT_THRESHOLD;
  467.         if (BWThreshold == 0) BWThreshold = 1;
  468.         break;
  469.         case KEY_INSERT:
  470.         BWThreshold -= INCREMENT_THRESHOLD;
  471.         if (BWThreshold == 0) BWThreshold = 1;
  472.         break;
  473.         default:
  474.         DrawIt = FALSE;
  475.         Tone(800, 100);
  476.         Tone(300, 200);
  477.         break;
  478.     }
  479.     }
  480.     while (GetK != KEY_ESC);
  481.  
  482.     CloseGraphDevice();
  483. }
  484.  
  485. /******************************************************************************
  486. * Routine to print (in text mode), current program status.              *
  487. ******************************************************************************/
  488. static void PrintSettingStatus(GifFileType *GifFile, GifRowType *DitherBuffer)
  489. {
  490.     char s[80];
  491.  
  492.     CloseGraphDevice();
  493.  
  494.     CPrintStr(PROGRAM_NAME, 1, INVERSE_ATTR);
  495.  
  496.     sprintf(s, "GIF File - %s", GifFileName);
  497.     CPrintStr(s, 3, NORMAL_ATTR);
  498.  
  499.     sprintf(s, "Gif Screen Size = [%d, %d]. Contains %d image(s).",
  500.     GifFile -> SWidth, GifFile -> SHeight, ImageNum);
  501.     CPrintStr(s, 5, NORMAL_ATTR);
  502.  
  503.     if (GifFile -> SColorMap)
  504.     sprintf(s,
  505.         "Has Screen Color map of %d bits. BackGround = [%d, %d, %d]",
  506.         GifFile -> SBitsPerPixel,
  507.         GifFile -> SColorMap[GifFile -> SBackGroundColor].Red,
  508.         GifFile -> SColorMap[GifFile -> SBackGroundColor].Green,
  509.         GifFile -> SColorMap[GifFile -> SBackGroundColor].Blue);
  510.     else
  511.     sprintf(s, "No Screen color map.");
  512.     CPrintStr(s, 7, NORMAL_ATTR);
  513.  
  514.     if (GifFile -> IColorMap)
  515.     sprintf(s, "Has Image map of %d bits (last image). Image is %s.",
  516.         GifFile -> IBitsPerPixel,
  517.         (GifFile -> IInterlace ? "interlaced" : "non interlaced"));
  518.     else
  519.     sprintf(s, "No Image color map.");
  520.     CPrintStr(s, 9, NORMAL_ATTR);
  521.  
  522.     sprintf(s, "Color to BW threshold level - %d%%.\n", BWThreshold / 255);
  523.     CPrintStr(s, 11, NORMAL_ATTR);
  524.  
  525.     CPrintStr("Color To BW mapping:", 15, NORMAL_ATTR);
  526.     switch(ColorToBWMapping) {
  527.     case C2BW_BACK_GROUND:
  528.         CPrintStr("Color != BackGround", 16, NORMAL_ATTR);
  529.         break;
  530.     case C2BW_GREY_LEVELS:
  531.         CPrintStr(".3 * R + .59 * G + .11 * B > threshold", 16,
  532.                                 NORMAL_ATTR);
  533.         break;
  534.     case C2BW_DITHER:
  535.         sprintf(s, ".3 * R + .59 * G + .11 * B dithered (Size = %d).",
  536.         DitherSize);
  537.         CPrintStr(s, 16, NORMAL_ATTR);
  538.         break;
  539.     }
  540.  
  541.     sprintf(s, "Dither Buffer %s (Size = %d), Zoom = %d.",
  542.     DitherBuffer ? "allocated succesfully" : "not allocated (failed)",
  543.     DitherSize, ZoomFactor);
  544.  
  545.     CPrintStr(s, 18, NORMAL_ATTR);
  546.  
  547.     CPrintStr("Press anything to continue:", 23, BLINK_ATTR);
  548.     MyGetCh();
  549.  
  550.     OpenGraphDevice();
  551. }
  552.  
  553. /******************************************************************************
  554. * Routine to cprintf given string centered at given Y level, and attr:        *
  555. ******************************************************************************/
  556. static void CPrintStr(char *Str, int y, int attr)
  557. {
  558.     gotoxy(40 - (strlen(Str) + 1) / 2, y);
  559.     textattr(attr);
  560.     cputs(Str);
  561. }
  562.  
  563. /******************************************************************************
  564. * Routine to set the position of Screen in Device, and what porsion of the    *
  565. * screen should be visible:                              *
  566. * MoveX, MoveY are the panning factors (if both zero - initialize).          *
  567. ******************************************************************************/
  568. static void SetPositon(int Why,
  569.                int ScreenWidth, int ScreenHeight,
  570.                int DeviceMaxX,  int DeviceMaxY,
  571.                int *ScreenLeft, int *ScreenTop,
  572.                int *DeviceLeft, int *DeviceTop,
  573.                int MoveX,       int MoveY)
  574. {
  575.  
  576.     MoveX /= ZoomFactor;       /* Make sure move same amount independent */
  577.     MoveY /= ZoomFactor;               /* of what ZoomFactor is. */
  578.  
  579.     /* Figure out position of GIF file in real device X axis: */
  580.     if (ScreenWidth * ZoomFactor <= DeviceMaxX + 1) {
  581.     /* Device is big enough to hold all the image X axis: */
  582.     *ScreenLeft = 0;
  583.     *DeviceLeft = (DeviceMaxX - ScreenWidth * ZoomFactor) / 2;
  584.     }
  585.     else {
  586.     /* Device is too small to hold all the image X axis: */
  587.     switch (Why) {
  588.         case SET_POSITION_RESET:
  589.         *ScreenLeft = 0;
  590.         break;
  591.         case SET_POSITION_ZOOM_U:
  592.         *ScreenLeft += DeviceMaxX / (2 * ZoomFactor);
  593.         break;
  594.         case SET_POSITION_ZOOM_D:
  595.         *ScreenLeft -= DeviceMaxX / (4 * ZoomFactor);
  596.         break;
  597.         case SET_POSITION_PAN:
  598.         if (MoveX != 0) *ScreenLeft += MoveX;
  599.         break;
  600.     }
  601.     if (*ScreenLeft < 0) *ScreenLeft = 0;
  602.     if ((ScreenWidth - *ScreenLeft) * ZoomFactor < DeviceMaxX + 1)
  603.         *ScreenLeft = (ScreenWidth * ZoomFactor -
  604.                         DeviceMaxX + 1) / ZoomFactor;
  605.     *DeviceLeft = 0;
  606.     }
  607.  
  608.     /* Figure out position of GIF file in real device Y axis: */
  609.     if (ScreenHeight * ZoomFactor <= DeviceMaxY + 1) {
  610.     /* Device is big enough to hold all the image Y axis: */
  611.     *ScreenTop = 0;
  612.     *DeviceTop = (DeviceMaxY - ScreenHeight * ZoomFactor) / 2;
  613.     }
  614.     else {
  615.     /* Device is too small to hold all the image Y axis: */
  616.     switch (Why) {
  617.         case SET_POSITION_RESET:
  618.         *ScreenTop = 0;
  619.         break;
  620.         case SET_POSITION_ZOOM_U:
  621.         *ScreenTop += DeviceMaxY / (2 * ZoomFactor);
  622.         break;
  623.         case SET_POSITION_ZOOM_D:
  624.         *ScreenTop -= DeviceMaxY / (4 * ZoomFactor);
  625.         break;
  626.         case SET_POSITION_PAN:
  627.         if (MoveY != 0) *ScreenTop += MoveY;
  628.         break;
  629.     }
  630.     if (*ScreenTop < 0) *ScreenTop = 0;
  631.     if ((ScreenHeight - *ScreenTop) * ZoomFactor < DeviceMaxY + 1)
  632.         *ScreenTop = (ScreenHeight * ZoomFactor -
  633.                          DeviceMaxY - 1) / ZoomFactor;
  634.     *DeviceTop = 0;
  635.     }
  636.  
  637.     /* Make sure the position is on Byte boundary (8 pixels per byte): */
  638.     *DeviceLeft &= 0xfff8;
  639. }
  640.  
  641. /******************************************************************************
  642. * Routine to clear graphic device:                          *
  643. ******************************************************************************/
  644. static void ClearGraphDevice(void)
  645. {
  646.     cleardevice();
  647. }
  648.  
  649. /******************************************************************************
  650. * Routine to open graphic device:                          *
  651. ******************************************************************************/
  652. static void OpenGraphDevice(void)
  653. {
  654.     int GraphDriver = HERCMONO, GraphMode = HERCMONOHI;
  655.  
  656.     if (registerbgidriver(Herc_driver) < 0)
  657.     GIF_EXIT("Cannt register graphic device.");
  658.  
  659.     initgraph(&GraphDriver, &GraphMode, "");
  660.     if (graphresult() != grOk)
  661.     GIF_EXIT("Graphics System Error (No Hercules!?).");
  662. }
  663.  
  664. /*****************************************************************************
  665. * Routine to close and shutdown    graphic    mode :                     *
  666. *****************************************************************************/
  667. static void CloseGraphDevice(void)
  668. {
  669.     closegraph();              /* Return the system to text mode. */
  670. }
  671.  
  672. /*****************************************************************************
  673. * Routine to evaluate dithered scanlines out of given ones, using Size         *
  674. * dithering matrix, starting from Row. The given scanlines are NOT modified. *
  675. *****************************************************************************/
  676. static void EvalDitheredScanline(GifRowType *ScreenBuffer, int Row,
  677.                      int RowSize, GifRowType *DitherBuffer)
  678. {
  679.     static char Dither2[2][2] = {     /* See Foley & Van Dam pp. 597-601. */
  680.     { 1, 3 },
  681.     { 4, 2 }
  682.     };
  683.     static char Dither3[3][3] = {
  684.     { 7, 9, 5 },
  685.     { 2, 1, 4 },
  686.     { 6, 3, 8 }
  687.     };
  688.     static char Dither4[4][4] = {
  689.     { 1,  9,  3,  11 },
  690.     { 13, 5,  15, 7 },
  691.     { 4,  12, 2,  10 },
  692.     { 16, 8,  14, 6 }
  693.     };
  694.     int i, j, k, Level;
  695.     long Intensity;
  696.     GifColorType *ColorMapEntry;
  697.  
  698.     /* Scan the Rows (Size rows) evaluate intensity every Size pixel and use */
  699.     /* the dither matrix to set the dithered result;                 */
  700.     for (i = 0; i <= RowSize - DitherSize; i += DitherSize) {
  701.     Intensity = 0;
  702.     for (j = Row; j < Row + DitherSize; j++)
  703.         for (k = 0; k < DitherSize; k++) {
  704.         ColorMapEntry = &ColorMap[ScreenBuffer[j][i+k]];
  705.         Intensity += 30 * ((int) ColorMapEntry->Red) +
  706.                  59 * ((int) ColorMapEntry->Green) +
  707.                  11 * ((int) ColorMapEntry->Blue);
  708.         }
  709.  
  710.     /* Find the intensity level (between 0 and Size^2) of our matrix: */
  711.     /* Expression is "Intensity * BWThreshold / (25500 * DefThresh)"  */
  712.     /* but to prevent from overflow in the long evaluation we do this:*/
  713.     Level = ((Intensity / 2550) * ((long) DEFAULT_THRESHOLD) /
  714.                         (((long) BWThreshold) * 10));
  715.     switch (DitherSize) {
  716.         case 2:
  717.         for (j = 0; j < DitherSize; j++)
  718.             for (k = 0; k < DitherSize; k++)
  719.             DitherBuffer[j][i+k] = Dither2[j][k] <= Level;
  720.         break;
  721.         case 3:
  722.         for (j = 0; j < DitherSize; j++)
  723.             for (k = 0; k < DitherSize; k++)
  724.             DitherBuffer[j][i+k] = Dither3[j][k] <= Level;
  725.         break;
  726.         case 4:
  727.         for (j = 0; j < DitherSize; j++)
  728.             for (k = 0; k < DitherSize; k++)
  729.             DitherBuffer[j][i+k] = Dither4[j][k] <= Level;
  730.         break;
  731.     }
  732.     }
  733. }
  734.  
  735. /******************************************************************************
  736. * The real drawing of the image is performed here. Few things are taken into  *
  737. * account:                                      *
  738. * 1. The zoom factor. If > 1 each pixel is multiplied this amount vertically  *
  739. *    and horizontally.                                  *
  740. * 2. The Invert flag. If TRUE each pixel before drawn is inverted.          *
  741. * 3. The rendering mode and dither matrix flag if dithering is selected.      *
  742. *   The image is drawn from ScreenBuffer ScreenTop/Left in the bottom/right   *
  743. * directions, onto the Device DeviceTop/Left in the bottom/right direction    *
  744. *     This routine was optimized for the hercules graphic card and should be  *
  745. * handled carfully as it is device dependent.                      *
  746. *   Pressing space during drawing will abort this routine.              *
  747. ******************************************************************************/
  748. static void DrawScreen(GifRowType *ScreenBuffer, GifRowType *DitherBuffer,
  749.     int DeviceTop, int DeviceLeft, int DeviceMaxX, int DeviceMaxY,
  750.     int ScreenTop, int ScreenLeft, int ScreenWidth, int ScreenHeight)
  751. {
  752.     unsigned int Offset;
  753.     int i, j, k, l, m, CountZoomJ, CountZoomI,
  754.     DitheredLinesLeft = 0, DitheredLinesCount, MapInvert[2];
  755.     GifByteType DeviceByte;
  756.     GifPixelType *Line;
  757.     GifColorType *ColorMapEntry;
  758.  
  759.     ClearGraphDevice();             /* Make sure we start from scratch. */
  760.  
  761.     if (InvertFlag) {           /* Make the inversion as fast a possible. */
  762.     MapInvert[0] = 1;
  763.     MapInvert[1] = 0;
  764.     }
  765.     else {
  766.     MapInvert[0] = 0;
  767.     MapInvert[1] = 1;
  768.     }
  769.  
  770.     for (CountZoomJ = ZoomFactor, j = ScreenTop, l = DeviceTop, DeviceByte = 0;
  771.      j < ScreenHeight && l <= DeviceMaxY; l++) {
  772.     Line = ScreenBuffer[j];
  773.  
  774.     /* We are going to access the hercules frame buffer directly: */
  775.     Offset = 0x2000 * (l & 0x03) + (l >> 2) * 90 + (DeviceLeft >> 3);
  776.  
  777.     /* Abort drawing if space bar was pressed: */
  778.     if (MyKbHit() && GetKey() == ' ') return;
  779.  
  780.     /* We decide right here what method to map Colors to BW so the inner */
  781.     /* loop will be independent of it (and therefore faster):         */
  782.     switch(ColorToBWMapping) {
  783.         case C2BW_BACK_GROUND:
  784.         for (CountZoomI = ZoomFactor, i = ScreenLeft, k = DeviceLeft, m = 0;
  785.              i < ScreenWidth && k <= DeviceMaxX;) {
  786.             /* The following lines are equivalent to the putpixel    */
  787.             /* (and making it real machine dependent...) by almost   */
  788.             /* factor of 3:                         */
  789.             /* putpixel(k++, l, MapInvert[ColorToBW(Line[i])]);         */
  790.             DeviceByte = (DeviceByte << 1) +
  791.                     MapInvert[Line[i] != BackGround];
  792.             if (++m == 8) {
  793.             /* We have byte - place it on hercules frame buffer: */
  794.             k += 8;
  795.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  796.             m = 0;
  797.             }
  798.  
  799.             if (!--CountZoomI) {
  800.             /* Go to next column: */
  801.             i++;
  802.             CountZoomI = ZoomFactor;
  803.             }
  804.         }
  805.         if (k < DeviceMaxX) {
  806.             /* Poke last byte also: */
  807.             DeviceByte <<= 8 - m;
  808.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  809.         }
  810.         break;
  811.         case C2BW_GREY_LEVELS:
  812.         for (CountZoomI = ZoomFactor, i = ScreenLeft, k = DeviceLeft, m = 0;
  813.              i < ScreenWidth && k <= DeviceMaxX;) {
  814.             /* The following lines are equivalent to the putpixel    */
  815.             /* (and making it real machine dependent...) by almost   */
  816.             /* factor of 3:                         */
  817.             /* putpixel(k++, l, MapInvert[ColorToBW(Line[i])]);         */
  818.  
  819.             ColorMapEntry = &ColorMap[Line[i]];
  820.             /* For the transformation from RGB to BW, see Folley &   */
  821.             /* Van Dam pp 613: The Y channel is the BW we need:         */
  822.             /* As colors are 255 maximum, the result can be up to    */
  823.             /* 25500 which is still in range of our 16 bits integers.*/
  824.             DeviceByte = (DeviceByte << 1) +
  825.             MapInvert[(30 * (int) ColorMapEntry->Red) +
  826.                    59 * ((int) ColorMapEntry->Green) +
  827.                    11 * ((int) ColorMapEntry->Blue) >
  828.                     BWThreshold];
  829.             if (++m == 8) {
  830.             /* We have byte - place it on hercules frame buffer: */
  831.             k += 8;
  832.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  833.             m = 0;
  834.             }
  835.  
  836.             if (!--CountZoomI) {
  837.             /* Go to next column: */
  838.             i++;
  839.             CountZoomI = ZoomFactor;
  840.             }
  841.         }
  842.         if (k < DeviceMaxX) {
  843.             /* Poke last byte also: */
  844.             DeviceByte <<= 8 - m;
  845.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  846.         }
  847.         break;
  848.         case C2BW_DITHER:
  849.         if (DitheredLinesLeft-- == 0) {
  850.             EvalDitheredScanline(ScreenBuffer,
  851.             (j < ScreenHeight - DitherSize ? j :
  852.                          ScreenHeight - DitherSize),
  853.             ScreenWidth, DitherBuffer);
  854.             DitheredLinesLeft = DitherSize - 1;
  855.             DitheredLinesCount = 0;
  856.         }
  857.         Line = DitherBuffer[DitheredLinesCount++];
  858.         for (CountZoomI = ZoomFactor, i = ScreenLeft, k = DeviceLeft, m = 0;
  859.              i < ScreenWidth && k <= DeviceMaxX;) {
  860.             /* The following lines are equivalent to the putpixel    */
  861.             /* (and making it real machine dependent...) by almost   */
  862.             /* factor of 3:                         */
  863.             /* putpixel(k++, l, MapInvert[Line[i]]);             */
  864.             DeviceByte = (DeviceByte << 1) + MapInvert[Line[i]];
  865.             if (++m == 8) {
  866.             /* We have byte - place it on hercules frame buffer: */
  867.             k += 8;
  868.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  869.             m = 0;
  870.             }
  871.  
  872.             if (!--CountZoomI) {
  873.             /* Go to next column: */
  874.             i++;
  875.             CountZoomI = ZoomFactor;
  876.             }
  877.         }
  878.         if (k < DeviceMaxX) {
  879.             /* Poke last byte also: */
  880.             DeviceByte <<= 8 - m;
  881.             pokeb(DEVICE_BASE, Offset++, DeviceByte);
  882.         }
  883.         break;
  884.     }
  885.  
  886.     if (!--CountZoomJ) {
  887.         /* Go to next row: */
  888.         j++;
  889.         CountZoomJ = ZoomFactor;
  890.     }
  891.     }
  892. }
  893.  
  894. /******************************************************************************
  895. * Walks along the current image, while printing pixel value and position.     *
  896. * 4 arrows may be used, and any other key will abort this operation          *
  897. * As there is no XOR mode for text, we copy all Page 0 to Page 1 before we    *
  898. * start this, and copy it back each time...                      *
  899. ******************************************************************************/
  900. static void DoCursorMode(GifRowType *ScreenBuffer,
  901.     int ScreenLeft, int ScreenTop, int ScreenWidth, int ScreenHeight,
  902.     int DeviceLeft, int DeviceTop)
  903. {
  904.     int GetK, DeviceRight, DeviceBottom, x, y, CursorTextY, Step;
  905.     GifPixelType Pixel;
  906.     char s[80];
  907.     char far *Page0, *Page1;
  908.  
  909.     Page0 = MK_FP(DEVICE_PAGE0, 0);
  910.     Page1 = MK_FP(DEVICE_PAGE1, 0);
  911.  
  912.     memcpy(Page1, Page0, 0x8000);
  913.  
  914.  
  915.     DeviceRight = DeviceLeft + (ScreenWidth - ScreenLeft) * ZoomFactor;
  916.     if (DeviceRight > HERC_MAX_X) DeviceRight = HERC_MAX_X;
  917.  
  918.     DeviceBottom = DeviceTop + (ScreenHeight - ScreenTop) * ZoomFactor;
  919.     if (DeviceBottom > HERC_MAX_Y) DeviceBottom = HERC_MAX_Y;
  920.  
  921.     x = (DeviceLeft + DeviceRight) / 2;
  922.     y = (DeviceTop + DeviceBottom) / 2;
  923.  
  924.     while (TRUE) {
  925.     Pixel = ScreenBuffer[ScreenTop + (y - DeviceTop) / ZoomFactor]
  926.                 [ScreenLeft + (x - DeviceLeft) / ZoomFactor];
  927.     sprintf(s, "Color = %d [%d, %d, %d], X = %d, Y = %d.",
  928.         Pixel,
  929.         ColorMap[Pixel].Red,
  930.         ColorMap[Pixel].Green,
  931.         ColorMap[Pixel].Blue,
  932.         (x - DeviceLeft) / ZoomFactor,
  933.         (y - DeviceTop) / ZoomFactor);
  934.     CursorTextY = (y > HERC_MAX_Y / 2 ? HERC_MAX_Y / 4 :
  935.                         3 * HERC_MAX_Y / 4);
  936.     setviewport(CURSOR_TEXT_X, CursorTextY,
  937.             CURSOR_TEXT_X + textwidth(s), CursorTextY + textheight(s),
  938.             TRUE);
  939.     clearviewport();
  940.     setviewport(0, 0, HERC_MAX_X, HERC_MAX_Y, TRUE);
  941.     setcolor(1);                 /* We only have one color here. */
  942.     line(0, y, HERC_MAX_X, y);
  943.     line(x, 0, x, HERC_MAX_Y);
  944.     outtextxy(CURSOR_TEXT_X, CursorTextY, s);
  945.     GetK = GetKey();
  946.  
  947.     memcpy(Page0, Page1, 0x8000);
  948.  
  949.     Step = 10;
  950.     switch (GetK) {
  951.         case '1':
  952.         GetK = KEY_END;
  953.         break;
  954.         case '2':
  955.         GetK = KEY_DOWN;
  956.         break;
  957.         case '3':
  958.         GetK = KEY_PGDN;
  959.         break;
  960.         case '4':
  961.         GetK = KEY_LEFT;
  962.         break;
  963.         case '6':
  964.         GetK = KEY_RIGHT;
  965.         break;
  966.         case '7':
  967.         GetK = KEY_HOME;
  968.         break;
  969.         case '8':
  970.         GetK = KEY_UP;
  971.         break;
  972.         case '9':
  973.         GetK = KEY_PGUP;
  974.         break;
  975.         default:
  976.         Step = 1;
  977.     }
  978.  
  979.     switch (GetK) {
  980.         case KEY_LEFT:
  981.         x -= Step;
  982.         break;
  983.         case KEY_RIGHT:
  984.         x += Step;
  985.         break;
  986.         case KEY_UP:
  987.         y -= Step;
  988.         break;
  989.         case KEY_DOWN:
  990.         y += Step;
  991.         break;
  992.         case KEY_PGUP:
  993.         y -= Step;
  994.         x += Step;
  995.         break;
  996.         case KEY_PGDN:
  997.         y += Step;
  998.         x += Step;
  999.         break;
  1000.         case KEY_HOME:
  1001.         y -= Step;
  1002.         x -= Step;
  1003.         break;
  1004.         case KEY_END:
  1005.         y += Step;
  1006.         x -= Step;
  1007.         break;
  1008.         default:
  1009.         return;
  1010.     }
  1011.     if (x < DeviceLeft) x = DeviceLeft;
  1012.     if (x >= DeviceRight) x = DeviceRight;
  1013.     if (y < DeviceTop) y = DeviceTop;
  1014.     if (y >= DeviceBottom) y = DeviceBottom;
  1015.     }
  1016. }
  1017.  
  1018. /******************************************************************************
  1019. * Return non zero value if at list one character exists in keyboard queue.    *
  1020. * This routine emulates kbhit() which do uses stdin and useless for us.       *
  1021. ******************************************************************************/
  1022. static int MyKbHit(void)
  1023. {
  1024.     return bioskey(1);
  1025. }
  1026.  
  1027. /******************************************************************************
  1028. * Get a key from keyboard directly (bypass stdin as we might redirect it).    *
  1029. * This routine emulates getch() which do uses stdin and useless for us.       *
  1030. ******************************************************************************/
  1031. static int MyGetCh(void)
  1032. {
  1033.     static int Extended = 0;
  1034.     int c;
  1035.  
  1036.     if (Extended) {
  1037.     c = Extended;
  1038.     Extended = 0;
  1039.     return c;
  1040.     }
  1041.     else {
  1042.     c = bioskey(0);
  1043.     if (c & 0x0ff)
  1044.         return c;
  1045.     else {
  1046.         Extended = c >> 8;
  1047.         return 0;
  1048.     }
  1049.     }
  1050. }
  1051.  
  1052. /******************************************************************************
  1053. * Get a key from keyboard, and translating operational keys into special      *
  1054. * codes (>255).    Lower case characters are upercased.                  *
  1055. ******************************************************************************/
  1056. static int GetKey(void)
  1057. {
  1058.     char c;
  1059.  
  1060.     while (TRUE) switch (c = MyGetCh()) {
  1061.     case 0:              /* Extended code - get the next extended char. */
  1062.         switch (MyGetCh()) {
  1063.         case 75: return KEY_LEFT;
  1064.         case 77: return KEY_RIGHT;
  1065.         case 72: return KEY_UP;
  1066.         case 80: return KEY_DOWN;
  1067.         case 71: return KEY_HOME;
  1068.         case 79: return KEY_END;
  1069.         case 73: return KEY_PGUP;
  1070.         case 81: return KEY_PGDN;
  1071.         case 83: return KEY_DELETE;
  1072.         case 82: return KEY_INSERT;
  1073.         }
  1074.         break;
  1075.     case 8:
  1076.         return KEY_BSPACE;
  1077.     case 10:
  1078.     case 13:
  1079.         return KEY_RETURN;
  1080.     case 27:
  1081.         return KEY_ESC;
  1082.     default:
  1083.         if (isprint(c)) {
  1084.         if (islower(c))
  1085.             return toupper(c);
  1086.         else
  1087.             return c;
  1088.         }
  1089.         else {
  1090.         Tone(800, 100);
  1091.         Tone(300, 200);
  1092.         }
  1093.     }
  1094.  
  1095.     return 0;                   /* Should never be here any case. */
  1096. }
  1097.  
  1098. /******************************************************************************
  1099. * Routine to make some sound with given Frequency, Time milliseconds:          *
  1100. ******************************************************************************/
  1101. static void Tone(int Frequency, int Time)
  1102. {
  1103.     if (BeepsDisabled) return;
  1104.  
  1105.     sound(Frequency);
  1106.     delay(Time);
  1107.     nosound();
  1108. }
  1109.